﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Data.Schema.Sql.SchemaModel;
using Microsoft.Data.Schema.SchemaModel;
using TFDP.Common.T4;

namespace TFDP.Common
{
    public static class TableExtensions
    {
        /// <summary>
        /// Check if the current instance contains the column with the given shortname
        /// </summary>
        /// <param name="table">The table instance</param>
        /// <param name="name">The shortname of the column</param>
        /// <returns>true if the column exists, otherwise false</returns>
        public static bool HasColumn(this ISqlTable table, string name)
        {
            var matches = from c in table.Columns
                          where c.IsNameEqual(name)
                          select c;

            return (matches.Count() == 1);
        }

        /// <summary>
        /// Obtain the column with the given shortname from the current instance
        /// </summary>
        /// <param name="table">The table instance</param>
        /// <param name="name">The shortname of the column</param>
        /// <returns>The column instance, null if it does not exist</returns>
        public static ISqlColumn GetColumn(this ISqlTable table, string name)
        {
            var matches = from c in table.Columns
                          where c.IsNameEqual(name)
                          select c;

            if (matches.Count() == 1)
                return matches.First();
            else
                return null;
        }

        /// <summary>
        /// Obtain the columns from the current table instance.
        /// Which columns get returned is determined by the flags that are specified.
        /// </summary>
        /// <param name="table">The current table instance</param>
        /// <param name="flags">The flags that specify which columns should be obtained</param>
        /// <returns>The specified columns for this table</returns>
        public static List<ISqlColumn> GetColumns(this ISqlTable table, ColumnFlags flags = ColumnFlags.All)
        {
            List<ISqlColumn> columns = new List<ISqlColumn>();

            var pkColumns = table.GetPrimaryKeys()
                .SelectMany(pk => pk.ColumnSpecifications)
                .Select(colspec => colspec.Column);

            var cols = from c in table.Columns
                       where
                       (
                           (
                                (flags.HasFlag(ColumnFlags.NormalColumns)) &&
                                (c is ISqlSimpleColumn) &&
                                (!pkColumns.Contains(c))
                            ) ||
                            (flags.HasFlag(ColumnFlags.PrimaryKeys) && pkColumns.Contains(c)) ||
                            //(flags.HasFlag(ColumnFlags.ForeignKeys) && fkColumns.Contains(c)) ||
                            (flags.HasFlag(ColumnFlags.ComputedColumns) && (c is ISqlComputedColumn))
                       ) ||
                       flags.HasFlag(ColumnFlags.All)
                       select c;

            if (flags.HasFlag(ColumnFlags.ExcludeIdentity))
            {
                cols = from c in cols
                       where (c is ISqlSimpleColumn && !((ISqlSimpleColumn)c).IsIdentity) || !(c is ISqlSimpleColumn)
                       select c;
            }

            return cols.Cast<ISqlColumn>().ToList<ISqlColumn>();
        }

        /// <summary>
        /// Obtain all the primary key constraints for the current table instance.
        /// </summary>
        /// <param name="table">The current table instance</param>
        /// <returns>All primary key constraints</returns>
        public static IEnumerable<ISqlPrimaryKeyConstraint> GetPrimaryKeys(this ISqlTable table)
        {
            var primaryKeys = from fk in table.Constraints
                              where fk is ISqlPrimaryKeyConstraint
                              select fk;

            return primaryKeys.Cast<ISqlPrimaryKeyConstraint>();
        }

        /// <summary>
        /// Obtain all the foreign keys for the current table instance
        /// </summary>
        /// <param name="table">The current table instance</param>
        /// <returns>All foreign keys</returns>
        public static IEnumerable<ISqlForeignKeyConstraint> GetForeignKeys(this ISqlTable table)
        {
            var foreignKeys = from fk in table.Constraints
                              where fk is ISqlForeignKeyConstraint
                              select fk;

            return foreignKeys.Cast<ISqlForeignKeyConstraint>();
        }

        /// <summary>
        /// Obtain the parent tables from the current instance
        /// </summary>
        /// <param name="table">The current instance</param>
        /// <returns>All tables that are referenced through foreign keys</returns>
        public static IEnumerable<ISqlTable> GetParents(this ISqlTable table)
        {
            var parents = from fk in table.Constraints
                          where fk is ISqlForeignKeyConstraint
                          select ((ISqlForeignKeyConstraint)fk).ForeignTable;

            // TODO: Determine if we need Distinct<T>() here or not.
            // I think not because if multiple foreign keys exist
            // all should be modeled and not a "unique" set
            return parents;
        }
    }

    [Flags]
    public enum ColumnFlags
    {
        All = 1,
        NormalColumns = 2,
        PrimaryKeys = 4,
        ComputedColumns = 8,
        ExcludeIdentity = 16
    }
}
